package com.redijedi.tapestry.components;

import org.apache.tapestry.Block;
import org.apache.tapestry.PrimaryKeyEncoder;
import org.apache.tapestry.annotations.BeginRender;
import org.apache.tapestry.annotations.Component;
import org.apache.tapestry.annotations.Parameter;
import org.apache.tapestry.annotations.Persist;
import org.apache.tapestry.annotations.SetupRender;
import org.apache.tapestry.corelib.components.Delegate;
import org.apache.tapestry.corelib.components.Loop;
import org.apache.tapestry.util.StringToEnumCoercion;

import com.redijedi.tapestry.internal.pagedloop.PagedSource;
import com.redijedi.tapestry.internal.pagedloop.PagerPosition;

/**
 * Provides a paged list similar to the grid component. However, this uses a ul
 * instead of a table by default. The list and its items are configurable via
 * parameters.
 * 
 * @author torr
 * 
 */
public class PagedLoop {

	@Parameter(value = "prop:componentResources.id", defaultPrefix = "literal")
	private String _id;

	@Parameter(value = "prop:componentResources.elementName", defaultPrefix = "literal")
	private String _elementName;

	@SuppressWarnings("unused")
	@Parameter(required = true)
	private Iterable<?> _source;

	private PagedSource<?> _pagedSource;

	/**
	 * Defines where the pager (used to navigate within the "pages" of results)
	 * should be displayed: "top", "bottom", "both" or "none".
	 */
	@Parameter(value = "bottom", defaultPrefix = "literal")
	private String _pagerPosition;

	private PagerPosition _internalPagerPosition;

	@Parameter("25")
	private int _rowsPerPage;

	@Persist
	private int _currentPage = 1;

	@SuppressWarnings("unused")
	@Parameter
	private Object _value;

	@SuppressWarnings("unused")
	@Parameter
	private boolean _volatile;

	@SuppressWarnings("unused")
	@Parameter
	private int _index;

	@SuppressWarnings("unused")
	@Parameter
	private PrimaryKeyEncoder<?, ?> _encoder;

	@SuppressWarnings("unused")
	@Component(parameters = { "source=pagedSource",
			"elementName=prop:elementName", "value=inherit:value",
			"volatile=inherit:volatile", "encoder=inherit:encoder",
			"index=inherit:index" })
	private Loop _loop;

	@Component(parameters = { "source=pagedSource", "rowsPerPage=rowsPerPage",
			"currentPage=currentPage" })
	private Pager _internalPager;

	@SuppressWarnings("unused")
	@Component(parameters = "to=pagerTop")
	private Delegate _pagerTop;

	@SuppressWarnings("unused")
	@Component(parameters = "to=pagerBottom")
	private Delegate _pagerBottom;

	@SuppressWarnings("unused")
	@Component
	private Element _element;

	/**
	 * A Block to render instead of the table (and pager, etc.) when the source
	 * is empty. The default is simply the text "There is no data to display".
	 * This parameter is used to customize that message, possibly including
	 * components to allow the user to create new objects.
	 */
	@Parameter(value = "block:empty")
	private Block _empty;

	public String getId() {
		return _id;
	}

	public String getElementName() {
		return _elementName;
	}

	public Object getPagerTop() {
		return _internalPagerPosition.isMatchTop() ? _internalPager : null;
	}

	public Object getPagerBottom() {
		return _internalPagerPosition.isMatchBottom() ? _internalPager : null;
	}

	public PagedSource<?> getPagedSource() {
		return _pagedSource;
	}

	public int getRowsPerPage() {
		return _rowsPerPage;
	}

	public void setRowsPerPage(int rowsPerPage) {
		_rowsPerPage = rowsPerPage;
	}

	public int getCurrentPage() {
		return _currentPage;
	}

	public void setCurrentPage(int currentPage) {
		_currentPage = currentPage;
	}

	@SuppressWarnings("unchecked")
	@SetupRender
	Object setup() {
		_internalPagerPosition = new StringToEnumCoercion<PagerPosition>(
				PagerPosition.class).coerce(_pagerPosition);

		_pagedSource = new PagedSource(_source);

		int availableRows = _pagedSource.getTotalRowCount();

		// If there's no rows, display the empty block placeholder.
		if (availableRows == 0) {
			return _empty;
		}

		int startIndex = (_currentPage - 1) * _rowsPerPage;
		int endIndex = Math.min(startIndex + _rowsPerPage - 1,
				availableRows - 1);

		_pagedSource.prepare(startIndex, endIndex);

		return null;
	}

	@BeginRender
	Object begin() {
		// Skip rendering of component (template, body, etc.) when there's
		// nothing to display.
		// The empty placeholder will already have rendered.
		return (_pagedSource.getTotalRowCount() == 0) ? false : true;
	}

	void onAction(int newPage) {
		// TODO: Validate newPage in range
		_currentPage = newPage;
	}

}
